package com.ys.web.verticel;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import com.ys.base.BaseConstants;
import com.ys.base.EnumType;
import com.ys.utils.base.http.McodeHandler;
import com.ys.utils.base.j2se.DateUtil;
import com.ys.utils.base.j2se.JsonUtil;
import com.ys.utils.base.j2se.Logger;
import com.ys.utils.base.j2se.StringHandler;
import com.ys.utils.help.GroupUtils;
import com.ys.utils.help.UserUtils;
import com.ys.utils.redis.Redis;
import com.ys.utils.redis.key.UserKey;
import com.ys.utils.spring.properties.Config;
import com.ys.web.sys.notice.mapper.SystemNoticeMapper;
import com.ys.web.sys.notice.po.SystemNoticePo;
import com.ys.web.sys.notice.vo.SystemNoticeVo;
import com.ys.web.ws.bean.MessageRequestBean;
import com.ys.web.ws.bean.MessageResponseBean;
import com.ys.web.ws.manager.ChatObjectManager;
import com.ys.web.ws.send.controller.MessageController;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author quxinjun
 * @version 1.0.0
 * @ClassName MainVerticel.java
 * @Description TODO
 * @createTime 2021年05月17日 09:28:00
 */
@Slf4j
@Component
public class WebSocketVerticle extends AbstractVerticle implements ApplicationRunner {

    @Autowired
    private SystemNoticeMapper systemNoticeMapper;

    @Autowired
    private MessageController messageController;

    @Override
    public void run(ApplicationArguments args) {
        Vertx.vertx().deployVerticle(this);
    }

    @Override
    public void start() throws Exception {
        HttpServer httpServer = vertx.createHttpServer();
        EventBus eventBus = vertx.eventBus();
        Router router = Router.router(vertx);
        router.route("/chat/").handler(StaticHandler.create());
        websocketMethod(httpServer, eventBus);
        httpServer.requestHandler(router::accept).listen(8081, res -> {
            if (res.succeeded()) {
                Logger.info("<~vert.x 初始化连接成功~>");
            }
            if (res.failed()) {
                Logger.info("<~vert.x 初始化连接失败~>");
            }
        });
    }

    public void websocketMethod(HttpServer server, EventBus eventBus) {
        //　WebSocket 连接
        server.websocketHandler(webSocket -> {
            String path = webSocket.path();
            String info = path.substring(path.lastIndexOf("/") + 1);
            String[] split = info.split("_");
            String userId = split[0];
            String terminal = split[1];
            String relationId = null;
            if (split.length > 2) {
                relationId = split[2];
            }
            long sysId = McodeHandler.from32Radix(userId);
//            Logger.info(UserUtils.getCacheUser(sss).getNickName() + " 连接socket");
            if (!(BaseConstants.SYSTEM_ANDROID.equals(terminal) || BaseConstants.SYSTEM_IOS.equals(terminal))) {
                Logger.error("用户：{" + userId + "}，终端传递出错");
                String str = UserUtils.getMessageResponseList(BaseConstants.SYSTEM_OBJECT_VALUE, null, EnumType.MessageCode.MESSAGE_TERMINAL_ERROR.getCode()
                        , "终端信息传递出错！", LocalDateTime.now().format(DateTimeFormatter.ofPattern(BaseConstants.BASE_FORMAT)));
                webSocket.writeTextMessage(str);
                webSocket.close();
                Logger.error("<------关闭 webSocket 连接--->");
            }

            int unReadSize = StringHandler.getInt(Config.pp.getOrDefault(BaseConstants.DataSourceConstants.READ_MESSAGE_SIZE, "500"));

            ServerWebSocket alreadySocket = ChatObjectManager.getConnectionMap().get(userId);
//            System.err.println("alreadySocket.textHandlerID():"+(StringHandler.isNotEmpty(alreadySocket)?alreadySocket.textHandlerID():""));
//            System.err.println("webSocket.textHandlerID():"+webSocket.textHandlerID());
            if (alreadySocket != null && !alreadySocket.textHandlerID().equals(webSocket.textHandlerID())) {
//                String str = UserUtils.getMessageResponseList(BaseConstants.SYSTEM_OBJECT_VALUE, null, EnumType.MessageCode.MESSAGE_LOGIN_TIP.getCode()
//                        , null, LocalDateTime.now().format(DateTimeFormatter.ofPattern(BaseConstants.BASE_FORMAT)));
//                alreadySocket.writeTextMessage(str);
                try {
                    alreadySocket.close();
                } catch (Exception e) {
                    Logger.error("关闭连接出现异常");
                }
            }

            //连接成功
            ChatObjectManager.getConnectionMap().put(userId, webSocket);
            //保存用户当前设备的 relationId 确保用户极光推送的准确性
            if (StringHandler.isNotEmpty(relationId)) {
                Redis.hash.hSet(UserKey.jPushRelationIdKey(), StringHandler.getString(sysId), relationId);
            }

            //记录用户连接 socket 的时间
            Redis.hash.hSet(UserKey.connectTimeKey(), StringHandler.getString(sysId), DateUtil.getDate(new Date(), "yyyyMMdd"));

            ChatObjectManager.getConnectionMapTime().put(userId, System.currentTimeMillis());

            String key = UserKey.terminalHash();
            Redis.hash.hSet(key, String.valueOf(sysId), terminal);

            List<MessageResponseBean> unReadMessage = messageController.getUnReadMessage(sysId);
            List<MessageResponseBean> userApplyList = messageController.getUserApplyList(sysId);
            unReadMessage.addAll(userApplyList);
            if (!unReadMessage.isEmpty()) {
                unReadMessage = unReadMessage.stream().sorted((o1, o2) -> {
                    if (StringHandler.isEmpty(o1.getCreateTime())) {
                        if (StringHandler.isEmpty(o2.getCreateTime())) {
                            return 0;
                        }
                        return 1;
                    }
                    if (StringHandler.isEmpty(o2.getCreateTime())) {
                        return -1;
                    }
                    return o1.getCreateTime().compareTo(o2.getCreateTime());
                }).collect(Collectors.toList());

                sendMessage(JsonUtil.toJson(unReadMessage.subList(0, Math.min(unReadMessage.size(), unReadSize))), webSocket);

                if (unReadMessage.size() > unReadSize) {
                    ChatObjectManager.getConnectionUnReadPage().add(sysId);
                }
            }

            ThreadUtil.execAsync(() -> {
                SystemNoticeVo systemNoticeVo = new SystemNoticeVo();

                systemNoticeVo.setId(IdUtil.randomUUID().replaceAll("-", ""));
                systemNoticeVo.setUserId(sysId);
                systemNoticeVo.setTitle("温馨提示");
                systemNoticeVo.setContent(Config.pp.get(BaseConstants.DataSourceConstants.LOGIN_PUSH_MESSAGE));
                systemNoticeVo.setCreateTime(DateUtil.getDate(new Date(), BaseConstants.BASE_FORMAT));
                String windowId = "SYS_NOTICE";
                MessageResponseBean messageResponseBean = new MessageResponseBean();
                messageResponseBean.setCode(EnumType.MessageCode.SYS_NOTICE.getCode());
                messageResponseBean.setFrom(windowId);
                messageResponseBean.setWindowId(windowId);
                messageResponseBean.setUserName(UserUtils.SYS_NOTICE.getNickName());
                messageResponseBean.setAvatarUrl(UserUtils.SYS_NOTICE.getAvatarUrl());
                messageResponseBean.setData(systemNoticeVo.getContent());
                messageResponseBean.setMessageId(systemNoticeVo.getId());
                messageResponseBean.setCreateTime(DateUtil.getDate(new Date(), BaseConstants.BASE_FORMAT));
                SystemNoticePo systemNoticePo = systemNoticeMapper.selectByUserId(sysId);
                if (systemNoticePo == null) {
                    int i = systemNoticeMapper.addNotice(systemNoticeVo);
                    if (i > 0) {
                        GroupUtils.sendCurrentOnLine(messageResponseBean, userId);
                    }
                }
            });


            String address = webSocket.textHandlerID();

            long timerID = vertx.setPeriodic(1000, o -> {
                MessageResponseBean messageResponseBean = new MessageResponseBean();
                messageResponseBean.setCode(EnumType.MessageCode.MESSAGE_CONFIRM.getCode());
                eventBus.send(address, JsonUtil.toJson(Collections.singletonList(messageResponseBean)));

            });

            //消息处理
            webSocket.frameHandler(handler -> {
                MessageRequestBean messageRequestBean = null;
                try {
                    //心跳检测：谁发过来的，发送给谁
                    messageRequestBean = JsonUtil.jsonToBean(handler.textData(), MessageRequestBean.class);
                } catch (Exception e) {

                }

                if (messageRequestBean != null) {
                    if (EnumType.MessageCode.MESSAGE_CONFIRM.getCode().equals(messageRequestBean.getCode())) {
                        //客户端连接确认
                        ChatObjectManager.getConnectionMapTime().put(userId, System.currentTimeMillis());
                    } else if (EnumType.MessageCode.MESSAGE_CHECK_HEART.getCode().equals(messageRequestBean.getCode())) {
                        //心跳检测：谁发过来的，发送给谁
                        ServerWebSocket serverWebSocket = ChatObjectManager.getConnectionMap().get(messageRequestBean.getFrom());
                        WebSocketVerticle.sendMessage(UserUtils.getMessageResponseList(messageRequestBean, null), serverWebSocket);
                    } else if (EnumType.MessageCode.MESSAGE_UNREAD_OVER.getCode().equals(messageRequestBean.getCode())) {
                        //客户端本次未读消息处理完成
                        try {
                            List<MessageResponseBean> unReadMessage2 = messageController.getUnReadMessage(sysId);
                            if (!unReadMessage2.isEmpty()) {
//                                    List<String> groupChatList = new ArrayList<String>(hgetall.values());
                                List<MessageResponseBean> collect = unReadMessage2.stream()
                                        .sorted((o1, o2) -> {
                                            if (StringHandler.isEmpty(o1.getCreateTime())) {
                                                if (StringHandler.isEmpty(o2.getCreateTime())) {
                                                    return 0;
                                                }
                                                return 1;
                                            }
                                            if (StringHandler.isEmpty(o2.getCreateTime())) {
                                                return -1;
                                            }
                                            return o1.getCreateTime().compareTo(o2.getCreateTime());
                                        }).collect(Collectors.toList());
                                sendMessage(JsonUtil.toJson(collect.subList(0, Math.min(collect.size(), unReadSize))), webSocket);
                                if (collect.size() <= unReadSize) {
                                    ChatObjectManager.getConnectionUnReadPage().remove(sysId);
                                }
                            }
                        } catch (IllegalStateException e) {
                            Logger.info(e.getMessage());
                        }
//                        }
                    } else if (EnumType.MessageCode.MESSAGE_UNREAD_CONFIRM.getCode().equals(messageRequestBean.getCode())) {
                        //修改信息状态
                        MessageRequestBean finalMessageRequestBean = messageRequestBean;
                        ThreadUtil.execAsync(() -> {
                            String unReadKey = UserKey.unReadKey(McodeHandler.from32Radix(finalMessageRequestBean.getFrom()));
                            Redis.hash.delHash(unReadKey, finalMessageRequestBean.getMessageId());

                            String userUnReadMessageEntityKey = UserKey.userUnReadMessageEntityKey(McodeHandler.from32Radix(finalMessageRequestBean.getFrom()));
                            Redis.hash.delHash(userUnReadMessageEntityKey, finalMessageRequestBean.getMessageId());
                        });

                    }
                }


            });
            // 客户端WebSocket 关闭时，将当前ID从map中移除
            webSocket.closeHandler(handler -> {
                ServerWebSocket serverWebSocket = ChatObjectManager.getConnectionMap().get(userId);
                if (StringHandler.isNotEmpty(serverWebSocket) && webSocket.textHandlerID().equals(serverWebSocket.textHandlerID()))
                    ChatObjectManager.removeChatMapByKey(userId);
//                Logger.info("主动关闭");
                ChatObjectManager.getCurrentOnline();
                UserUtils.lastLogoutTime(sysId);
                vertx.cancelTimer(timerID);
            });

            webSocket.exceptionHandler(handler -> {

                ServerWebSocket serverWebSocket = ChatObjectManager.getConnectionMap().get(userId);
                if (StringHandler.isNotEmpty(serverWebSocket) && webSocket.textHandlerID().equals(serverWebSocket.textHandlerID()))
                    ChatObjectManager.removeChatMapByKey(userId);
//                Logger.info("异常关闭");
                ChatObjectManager.getCurrentOnline();
                UserUtils.lastLogoutTime(sysId);
                vertx.cancelTimer(timerID);

            });
        });


    }

    public static boolean sendMessage(String content, ServerWebSocket serverWebSocket) {
        try {
            serverWebSocket.writeTextMessage(content);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean sendMessage(ServerWebSocket serverWebSocket, MessageResponseBean messageResponseBean) {
        try {
            List<MessageResponseBean> list = new ArrayList<>();
            list.add(messageResponseBean);
            String jsonStr = JsonUtil.toJson(list);
            serverWebSocket.writeTextMessage(jsonStr);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}
